package sim1011.status;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Random;

import sim1011.starter.EctoMobileStarter;
import sim1011.starter.Logger;

public class NetworkMatrix {
	public static boolean mobileReachable = true;
	// variabili per lo spostamento del nodo
	private double base_prob = 0.5d;

	private String branch = "";
	private String last_last_move = "";
	private String last_move = "";
	private int n = 0;
	private HashMap<String, Integer> networkLinkQoS;
	private double prob_backward = 0.5d;
	private double prob_forward = 0.5d;

	public NetworkMatrix() {
		networkLinkQoS = new HashMap<String, Integer>();
	}

	public void addNewLink(String nodeA, String nodeB, int QoS) {
		if (!(networkLinkQoS.containsKey(nodeA + "-" + nodeB) || networkLinkQoS
				.containsKey(nodeB + "-" + nodeA)) && !nodeA.equals(nodeB)) {
			networkLinkQoS.put(nodeA + "-" + nodeB, QoS);
		}
	}

	private void fillNetworkLinkQoSAfterUnreach(String mobileNode) {
		Iterator<String> iterator = networkLinkQoS.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			if (key.contains(mobileNode)) {
				Integer deltaMove = EctoMobileStarter.r
						.nextInt(EctoMobileStarter.MAX_DELTA_MOVE);
				Integer newQoS = 30 + deltaMove;
				if (newQoS > EctoMobileStarter.MAX_NETWORK_LINK_QOS) {
					networkLinkQoS.put(key,
							EctoMobileStarter.MAX_NETWORK_LINK_QOS);
				} else {
					networkLinkQoS.put(key, newQoS);
				}
				Logger.log("[NETWORKMATRIX] Ritorno dall'unricciabilita': nuovo valore di Qos per: "
						+ key + " è: " + networkLinkQoS.get(key));
			}
		}
	}

	public Integer getLinkQoS(String nodeA, String nodeB) {
		if (networkLinkQoS.containsKey(nodeA + "-" + nodeB)) {
			return networkLinkQoS.get(nodeA + "-" + nodeB);
		} else if (networkLinkQoS.containsKey(nodeB + "-" + nodeA)) {
			return networkLinkQoS.get(nodeB + "-" + nodeA);
		} else {
			return null;
		}
	}

	private void goBackward(String mobileNode) {
		if (last_move.equals("b")) {
			prob_backward = base_prob + (1 / Math.pow(2.0d, (double) n));
			prob_forward = base_prob - (1 / Math.pow(2.0d, (double) n));
		} else {
			prob_backward = base_prob - (1 / Math.pow(2.0d, (double) n));
			prob_forward = base_prob + (1 / Math.pow(2.0d, (double) n));
		}
		Iterator<String> iterator = networkLinkQoS.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			if (key.contains(mobileNode)) {
				Integer deltaMove = EctoMobileStarter.r
						.nextInt(EctoMobileStarter.MAX_DELTA_MOVE);
				Integer currentQoS = networkLinkQoS.get(key);
				Integer newQoS = currentQoS - deltaMove;
				if (newQoS <= EctoMobileStarter.MIN_NETWORK_LINK_QOS) {
					networkLinkQoS.put(key,
							EctoMobileStarter.MIN_NETWORK_LINK_QOS + 1);
				} else {
					networkLinkQoS.put(key, newQoS);
				}
				Logger.log("[NETWORKMATRIX] Vado indietro: nuovo valore di Qos per: "
						+ key + " è: " + networkLinkQoS.get(key));
			}
		}
	}

	private void goForward(String mobileNode) {
		if (last_move.equals("f")) {
			prob_forward = base_prob + (1 / Math.pow(2.0d, (double) n));
			prob_backward = base_prob - (1 / Math.pow(2.0d, (double) n));
		} else {
			prob_forward = base_prob - (1 / Math.pow(2.0d, (double) n));
			prob_backward = base_prob + (1 / Math.pow(2.0d, (double) n));
		}
		Iterator<String> iterator = networkLinkQoS.keySet().iterator();
		while (iterator.hasNext()) {
			String key = iterator.next();
			if (key.contains(mobileNode)) {
				Integer deltaMove = EctoMobileStarter.r
						.nextInt(EctoMobileStarter.MAX_DELTA_MOVE);
				Integer currentQoS = networkLinkQoS.get(key);
				Integer newQoS = currentQoS + deltaMove;
				if (newQoS > EctoMobileStarter.MAX_NETWORK_LINK_QOS) {
					networkLinkQoS.put(key,
							EctoMobileStarter.MAX_NETWORK_LINK_QOS);
				} else {
					networkLinkQoS.put(key, newQoS);
				}
				Logger.log("[NETWORKMATRIX] Vado avanti: nuovo valore di Qos per: "
						+ key + " è: " + networkLinkQoS.get(key));
			}
		}
	}

	public void printMatrix() {
		Iterator<String> iterator = networkLinkQoS.keySet().iterator();
		while (iterator.hasNext()) {
			String link = iterator.next();
			Logger.log("[NETWORKMATRIX] " + link + " : "
					+ networkLinkQoS.get(link));
		}
	}

	// metodi per roam()
	private void restart() {
		// System.out.println("Restart!!");
		base_prob = 0.5d;
		prob_forward = 0.5d;
		prob_backward = 0.5d;
		n = 0;
		branch = "";
		last_last_move = "";
		last_move = "";
	}

	public void roam(String mobileNode) {
		int k = EctoMobileStarter.r.nextInt(100);
		if (k < 80) {
			// se sono appena rientrato
			if (!NetworkMatrix.mobileReachable) {
				fillNetworkLinkQoSAfterUnreach(mobileNode);
			}
			NetworkMatrix.mobileReachable = true;
			Random r = new Random(System.currentTimeMillis());
			int rand = r.nextInt(10001);
			double prob = (double) rand / 10000;
			//System.out.println("Prob: " + prob);
			n++;
			if (prob < prob_forward) {
				last_move = "f";
				goForward(mobileNode);
			} else {
				last_move = "b";
				goBackward(mobileNode);
			}
			if (n == 1) {
				branch = last_move;
				// System.out.println("Ho preso il branch: " + branch);
			}
			if (last_move.equals(last_last_move) && !last_move.equals(branch)) {
				restart();
			}
			last_last_move = last_move;
		} else {
			// il nodo mobile ha perso il collegamento
			restart();
			Iterator<String> iterator = networkLinkQoS.keySet().iterator();
			NetworkMatrix.mobileReachable = false;
			while (iterator.hasNext()) {
				String key = iterator.next();
				if (key.contains(mobileNode)) {
					networkLinkQoS.put(key,
							EctoMobileStarter.MIN_NETWORK_LINK_QOS);
					Logger.log("[NETWORKMATRIX] nuovo valore di Qos per: "
							+ key + " è: " + networkLinkQoS.get(key));
				}
			}
		}
	}
}